/***********************************************************************************************************************
**	filename: serial_port.c
**	author: Echo Zhang @2021-05-08
**  e-mail: not_xx@126.com
***********************************************************************************************************************/

#include "user_types.h"

#define DBG_SERIAL_PORT
#if !defined(DBG_SERIAL_PORT)
#undef DBG_MODE
#endif

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>  
#include <sys/time.h>
#include <termios.h>
#include <unistd.h> 
#include <sys/stat.h> 

#include "dbg.h"
#include "tick.h"
#include "serial_port.h"

int _Fd = -1;
SerialPort_Recv_FT _RecvFunc;
struct timeval serial_timeout;

si32 serial_port_set_baud(int fd, si32 baud)
{
    int baudArr;
    struct termios termios;
    dbg_msg("serial_port_set_baud: %d", baud);
    return_val_if_failed(fd > 2, -402);
    switch(baud) {
    case 1152000:
        baudArr = B1152000;
        break;
    case 1000000:
        baudArr = B1000000;
        break;
    case 230400:
        baudArr = B230400;
        break;
    case 115200:
        baudArr = B115200;
        break;
    case 57600:
        baudArr = B57600;
        break;
    case 38400:
        baudArr = B38400;
        break;
    case 19200:
        baudArr = B19200;
        break;
    case 9600:
        baudArr = B9600;
        break;
    case 4800:
        baudArr = B4800;
        break;
    case 1200:
        baudArr = B1200;
        break;
    default:
        baudArr = B1152000;
        break;
    }

    if (tcgetattr(fd, &termios) == -1) {
        sys_err("failed to func tcgetattr, fd = %d", fd);
        return -1;
    }

    cfsetispeed(&termios, baudArr);   
    cfsetospeed(&termios, baudArr);    

    termios.c_cflag |= CLOCAL;  
    termios.c_cflag |= CREAD;  
    termios.c_cflag &= ~CSIZE;  
    termios.c_cflag |= CS8; 
    termios.c_cflag &= ~PARENB;
    termios.c_iflag &= ~ICRNL; 
    termios.c_cflag &= ~CSTOPB; 
    termios.c_oflag &= ~OPOST;
    termios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    termios.c_iflag &= ~IXON;
        
    // 设置等待时间和最小接收字符  
    termios.c_cc[VTIME] = 1;    /* 读取一个字符等待1*(1/10)s */    
    termios.c_cc[VMIN] = 1;     /* 读取字符的最少个数为1 */  
    tcflush(fd, TCIFLUSH);  

    if (tcsetattr(fd, TCSANOW, &termios) != 0) {
        sys_err("failed to func tcsetattr, fd = %d", fd);
        return -1;
    }
    return 0;
}

si32 serial_port_open(const char *serialPort, si32 baud)
{
    int fd;
    fd = open(serialPort, O_RDWR);
    return_val_if_failed(fd > 2, -400);

    if(serial_port_set_baud(fd, baud) < 0) {
        close(fd);
        dbg_err("failed to func serial_port_set_baud");
        return -401;
    }
    //刷清串口缓存
    tcflush(fd,TCIOFLUSH);
    return fd;
}

int serial_port_init(const char *devName, ui32 baud)
{
    _Fd = serial_port_open(devName, baud);
    dbg_msg("open serial port %s: %s", devName,  _Fd > 0 ? "success" : "failed");
    return _Fd;
}

void serial_port_send_data(const ui8 buffer[], Size_DT bufferLen)
{ 
    if(write(_Fd, buffer, bufferLen) <= 0) {
        dbg_err("failed to write serial port!");
        return;
    }    
    dbg_print_bytes_as_hex("send", buffer, bufferLen);
}

void serial_port_routine_thread(void)
{
    static ui8 _buffer[10240];
    static Size_DT _bufferLen = 0;
    static Tick_DT _recvTick = 0;

    int len = read(_Fd, &_buffer[_bufferLen], sizeof(_buffer)-_bufferLen);
    if(len > 0) {
        _bufferLen += len;
        if(_bufferLen >= sizeof(_buffer)) {
            _RecvFunc(_buffer, _bufferLen);
            _bufferLen = 0;
        }
        _recvTick = tick_get();
    } else {
        if(_bufferLen>0 && tick_timeout(_recvTick, 250)) {
            _RecvFunc(_buffer, _bufferLen);
            _bufferLen = 0;
        }
    }
}

int serialport_readbyte(uint8_t *buffer)
{
	int fs_sel;
	fd_set fs_read;
	struct timeval time;
	
	FD_ZERO(&fs_read);
	FD_SET(_Fd, &fs_read);
	time.tv_sec = 0;
	time.tv_usec = 100000;
	fs_sel = select(_Fd + 1, &fs_read, NULL, NULL, &time);
	if (fs_sel > 0) {
		if (read(_Fd, (char *)buffer, 0x01) > 0) {
            return 0;
        } 
	}

    return -1;
}

/***********************************************************************************************************************
**	endline
***********************************************************************************************************************/
